﻿using CommonDevelop.Operator;
using CommonEngine;
using CommonFP;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace CommonDevelop.DB
{
    /// <summary>
    /// 服务层基类
    /// (服务层与仓储层在切面上是映射关系一对一的，但真实的服务层是可以Ioc适配的，在到达控制器之前都可以安排数据布局)
    /// 业务层DDD就是表达IBaseService与IBase之间的关系，AOP则是由多个IBase派生来完成
    /// </summary>
    /// <typeparam name="T">DB实体</typeparam>
    /// <typeparam name="TRepository">DB仓储类</typeparam>
    public abstract class BaseService<T, TDB> : IBaseService<T> where T : BaseEn, new() where TDB : IBase<T>
    {
        protected readonly TDB repository_DB;
        protected readonly IHttpContextAccessor http_ContextAccessor;
        /// <summary>
        /// 基类构造注入，数据层对应的业务切面
        /// </summary>
        public BaseService(TDB repository)
        {
            this.repository_DB = repository;
            this.http_ContextAccessor = IocContainer.Resolve<IHttpContextAccessor>();
        }


        public virtual string IP()
            => FP.Try(() => this.http_ContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString())
            .Run().ToValidation().Outcome();

        public virtual string BuildSeq()
            => string.Empty;

        public virtual string BusinessKey()
            => JwtHelper.GetBusinessID();

        public virtual bool CleanUp()
            => false;


        public virtual IEnumerable<T> GetList(string keyword = null, bool IsDelete = false)
            => FP.Try(() => this.repository_DB.GetList(keyword, IsDelete))
            .Run()
            .Match(e => Enumerable.Empty<T>(), a => a);

        public virtual IEnumerable<T> GetPageList(Pagination Pag, string keyword = null, bool IsDelete = false)
            => FP.Try(() => this.repository_DB.GetPageList(Pag, keyword, IsDelete))
            .Run()
            .Match(e => Enumerable.Empty<T>(), a => a);

        public virtual IEnumerable<TVO> GetList<TVO>(string keyword = null, bool IsDelete = false)
            where TVO : class, new()
            => this.GetList(keyword, IsDelete).MapToList<T, TVO>();

        public virtual IEnumerable<TVO> GetPageList<TVO>(Pagination Pag, string keyword = null, bool IsDelete = false)
            where TVO : class, new()
            => this.GetPageList(Pag, keyword, IsDelete).MapToList<T, TVO>();


        public virtual async Task<T> GetEntity(object keyValue)
            => await FP.Async(this.repository_DB.GetEntity(keyValue));

        public virtual async Task<T> GetEntity(Expression<Func<T, bool>> expression, bool IsDelete)
            => await FP.Async(this.repository_DB.GetEntity(expression, IsDelete));



        public virtual Validation<bool> Add_En(T En)
            => FP.Try(() =>
            {
                En.Modified(DateTime.Now);
                return this.repository_DB.Add_Entity(En, true);
            }).Run().ToValidation();

        public virtual Validation<bool> Add_En(IEnumerable<T> EnLs)
            => FP.Try(() =>
            {
                EnLs.ForEach(a => a.Modified(DateTime.Now));
                return this.repository_DB.Add_Entities(EnLs, true) > 0;
            }).Run().ToValidation();


        public virtual Validation<bool> Save_En(T En)
            => FP.Try(() =>
            {
                En.Modified(DateTime.Now);
                return this.repository_DB.Update_Entity(En, true);
            }).Run().ToValidation();

        public virtual Validation<bool> Save_En(IEnumerable<T> EnLs)
            => FP.Try(() =>
            {
                EnLs.ForEach(a => a.Modified(DateTime.Now));
                return this.repository_DB.Update_Entities(EnLs, true) > 0;
            }).Run().ToValidation();


        public virtual Validation<bool> VirtualDelete(object keyValue, string UserId, string UserName)
            => FP.Try(() =>
            {
                if (keyValue != null)
                {
                    var en = SugarBizDB.Set_PrimaryKeyValue<T>(keyValue);
                    if (en != null)
                    {
                        en.IsDelete = false;
                        en.LastModified = CommonOP.GetIntByDT(DateTime.Now);
                        en.ModifyUserId = UserId.IsEmpty() ? "" : UserId;
                        en.ModifyUserName = UserName.IsEmpty() ? "" : UserName;
                        Expression<Func<T, object>> columns = a => new { a.IsDelete, a.LastModified, a.ModifyUserId, a.ModifyUserName };
                        this.repository_DB.Update_EntityColumns(en, columns);
                        return true;
                    }
                }
                return false;
            }).Run().ToValidation();

        public virtual Validation<bool> Delete(object keyValue)
            => FP.Try(() => keyValue
            .Pipe(obj => SugarBizDB.Set_PrimaryKeyValue<T>(obj))
            .Pipe(this.repository_DB.Delete_Entity))
            .Run().ToValidation();



        public virtual Option<TVO> Get<TVO>(object keyValue)
            where TVO : class, new()
        {
            var v = from en in this.GetEntity(keyValue)
                    select en.MapTo<TVO>();

            return v.OrElse(() => FP.Async(default(TVO))).Map(a => a.ToOption()).Result;
        }

        public virtual Option<TVO> GetMap<TVO>(T en)
            where TVO : class, new()
            => en.ToOption().Map(a => a.MapTo<TVO>());

        public virtual IEnumerable<TVO> GetMapList<TVO>(IEnumerable<T> EnList)
           where TVO : class, new()
           => EnList.MapToList<T, TVO>();

        public virtual IEnumerable<TVO> GetBind<TVO>(T en)
            where TVO : class, new()
            => FP.Try(() => this.repository_DB.GetPull<TVO>(en))
            .Run().Match(e => Enumerable.Empty<TVO>(), a => a);


        public virtual Validation<bool> Add<TVO>(TVO En)
            where TVO : class, new()
            => En.ToOption()
                .Map(a => a.MapTo<T>())
                .Map(a => this.Add_En(a))
                .Outcome();

        /// <summary>
        /// 保存视图（实体结构一致）（非FP）
        /// </summary>
        public virtual Validation<bool> Save<TVO>(TVO En)
            where TVO : class, new()
        {
            TVO result = En as TVO;
            var pipe1 = FP.MakePipe<TVO, T>(a => FP.Async(a.MapTo<T>()));
            var pipe3 = FP.MakePipe<TVO, T>(a => this.GetEntity(SugarBizDB.Get_DtoKeyValue<T>(a)));

            var v = from Dto in FP.Async(result.IsValidData())
                    from uB in pipe1(Dto)
                    from olduB in pipe3(Dto)
                    let NewuB = olduB.MergeEn(uB)
                    let _ = FP.ToFunc(() => NewuB.Modified(DateTime.Now))
                    let b = this.repository_DB.Update_Entity(NewuB, true)
                    select b;

            var t = v.Recover(ex => FP.Error(ex.Message));
            t.Wait();

            return t.Result;
        }
    }
}
